﻿
using System.Linq;
using Patterns4Net.ArchitectureExplorer.Infrastructure;

namespace Patterns4Net.ArchitectureExplorer.ViewModels
{
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Diagnostics.Contracts;
    using Caliburn.Micro;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Messages;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Results;
    using Patterns4Net.ArchitectureExplorer.ViewModels.Services;

    [Export(typeof(ShellViewModel))]
    public class ShellViewModel : Screen, IHandle<ErrorMessage>
    {
        private readonly IWindowManager windowManager;

        private readonly IEventAggregator eventAggregator;

        private readonly IMessageBoxService messagesBoxService;

        private readonly IOpenFileDialog openFileDialog;

        private readonly IVisualStudioFilesParser visualStudioFilesParser;

        [ImportingConstructor]
        public ShellViewModel(
            IWindowManager windowManager,
            IEventAggregator eventAggregator,
            IMessageBoxService messagesBoxService,
            IOpenFileDialog openFileDialog,
            IVisualStudioFilesParser visualStudioFilesParser)
        {
            Contract.Requires(windowManager != null);
            Contract.Requires(eventAggregator != null);
            Contract.Requires(messagesBoxService != null);
            Contract.Requires(openFileDialog != null);

            eventAggregator.Subscribe(this);
            this.windowManager = windowManager;
            this.eventAggregator = eventAggregator;
            this.messagesBoxService = messagesBoxService;
            this.openFileDialog = openFileDialog;
            this.visualStudioFilesParser = visualStudioFilesParser;
            this.Logger = new NullLogger();
            this.StartNewDiagram();
        }

        [Import]
        public ILog Logger { get; set; }

        /* -------- Panels -------------- */

        [Import]
        public DiagramViewModel DiagramViewModel { get; set; }

        [Import]
        public DiagramBrowserViewModel DiagramBrowser { get; set; }

        [Import]
        public PatternDocumentationPanelViewModel PatternDocumentation { get; set; }

        [Import]
        public DetailPanelViewModel DetailPanel { get; set; }

        [Import]
        public OutputWindowViewModel OutputWindow { get; set; }

        [Import]
        public ErrorsWindowViewModel ErrorsWindow { get; set; }

        /* ------ Dialogs --------------- */

        [Import]
        public SettingsViewModel SettingsViewModel { get; set; }


        /* ------- Menu actions --------- */
        public void StartNewDiagram()
        {
            this.eventAggregator.Publish(new StartNewDiagramMessage());
        }

        public void Forward()
        {
            this.eventAggregator.Publish(new GoForwardMessage());
        }

        public void Back()
        {
            this.eventAggregator.Publish(new GoBackMessage());
        }

        public void ZoomOut()
        {
            this.eventAggregator.Publish(new ZoomOutMessage());
        }

        public IEnumerable<IResult> LoadAssembly()
        {
            Contract.Requires(this.DiagramViewModel != null);

            var dialog = (IOpenFileDialog)this.openFileDialog.Clone();
            dialog.Multiselect = true;
            dialog.AddExtension(".NET Assembly (*.dll, *.exe)", "*.dll;*.exe");

            var filenames = dialog.ShowDialog();
            if (filenames == null)
            {
                return Enumerable.Empty<IResult>();
            }

            return this.LoadAssemblies(filenames);
        }

        public IEnumerable<IResult> LoadSolutionFile()
        {
            var dialog = (IOpenFileDialog)this.openFileDialog.Clone();
            dialog.Multiselect = true;
            dialog.AddExtension("C# Visual Studio solution (*.sln)", "*.sln");

            var filenames = dialog.ShowDialog();
            if (filenames == null)
            {
                return Enumerable.Empty<IResult>();
            }

            var projects = filenames.SelectMany(x => this.visualStudioFilesParser.GetProjects(x));
            return this.LoadProjectFiles(projects);
        }

        public IEnumerable<IResult> LoadProjectFile()
        {
            var dialog = (IOpenFileDialog)this.openFileDialog.Clone();
            dialog.Multiselect = true;
            dialog.AddExtension("C# Visual Studio projects (*.csproj)", "*.csproj");

            var filenames = dialog.ShowDialog();
            if (filenames == null)
            {
                return Enumerable.Empty<IResult>();
            }

            return this.LoadProjectFiles(filenames);
        }

        public void ShowSettings()
        {
            this.windowManager.ShowDialog(this.SettingsViewModel);
        }

        public void ShowAbout()
        {
            this.windowManager.ShowDialog(new AboutDialogViewModel());
        }

        public IEnumerable<IResult> RunPatternEnforcer()
        {
            yield return Progress.Show();
            yield return new RunPatternEnforcer(this.DiagramViewModel.Diagram);
            yield return Progress.Hide();
        }

        public void Exit()
        {
            this.TryClose();
        }

        public void Handle(ErrorMessage message)
        {
            if (message.ShowAsWarning)
            {
                this.messagesBoxService.ShowMessageBox(
                    message.Message,
                    "Warning",
                    System.Windows.MessageBoxButton.OK,
                    System.Windows.MessageBoxImage.Warning);                
            }
            else
            {
                this.messagesBoxService.ShowMessageBox(
                    message.Message,
                    "Error in Application",
                    System.Windows.MessageBoxButton.OK,
                    System.Windows.MessageBoxImage.Error);
            }
        }

        private IEnumerable<IResult> LoadAssemblies(string[] filenames)
        {
            yield return Progress.Show();

            foreach (var filename in filenames)
            {
                yield return new LoadAssembly(filename, this.DiagramViewModel.Diagram);
            }

            this.eventAggregator.Publish(new ZoomToTopLevelMessage());
            this.NotifyOfPropertyChange(() => this.DiagramViewModel);
            yield return Progress.Hide();
        }

        private IEnumerable<IResult> LoadProjectFiles(IEnumerable<string> filenames)
        {
            var assemblies = new List<string>();
            foreach (var filename in filenames)
            {
                try
                {
                    var assembly = this.visualStudioFilesParser.GetAssemblyFilename(filename);
                    assemblies.Add(assembly);
                }
                catch (AssemblyFileNotFound ex)
                {
                    this.eventAggregator.Publish(new ErrorMessage("The assembly file was not found. Have you build the project?"));
                    this.Logger.Error(ex);
                }
                catch (VisualStudioParserException ex)
                {
                    this.eventAggregator.Publish(new ErrorMessage("The project file cannot be parsed. Detailed information have been logged."));
                    this.Logger.Error(ex);
                }
            }

            return this.LoadAssemblies(assemblies.ToArray());
        }
    }
}
